from functools import wraps
import logging

import aiohttp

from webutils.logging import ContextAdapter

logger = logging.getLogger('server.connection')


class ConnectionProxy:
    def __init__(self, application, log_context=None):
        self.application = application
        self._db_connection = {}
        self._http_session = None
        self.log_context = log_context or {}
        self.logger = ContextAdapter(logger, log_context)

    @property
    def http_session(self):
        if not self._http_session:
            self._http_session = aiohttp.ClientSession()
        return self._http_session

    @property
    def db_connection(self):
        return self._db_connection

    @property
    def settings(self):
        return self.application.settings

    async def get_connection(self, db_name):
        if db_name not in self._db_connection:
            db_proxy = self.settings['DB_MAPPING'][db_name]
            connection = await db_proxy.get_connection()
            self._db_connection[db_name] = connection
            self.logger.debug('connection "%s" is created', db_name)
        return self._db_connection[db_name]

    async def close(self):
        for db_name, db_connection in self._db_connection.items():
            db_proxy = self.settings['DB_MAPPING'][db_name]
            await db_proxy.close_connection(db_connection)
            self.logger.debug('connection "%s" is closed', db_name)

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc, trace_back):
        # TODO: add log
        await self.close()


def connection_proxy(f):
    @wraps(f)
    async def wrapper(handler, *args, **kwargs):
        application = handler.application
        log_context = getattr(handler, 'log_context', {})
        async with ConnectionProxy(application,
                                   log_context=log_context) as conn:
            return await f(handler, conn, *args, **kwargs)

    return wrapper
